/**
 * 版权所有(C)，上海勾芒信息科技，2017，所有权利保留。
 * 
 * 项目名：	cards-web
 * 文件名：	WxMessageRouterRule.java
 * 模块说明：	
 * 修改历史：
 * 2017年5月31日 - Debenson - 创建。
 */
package me.chanjar.weixin.message.rule;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import com.alex.wechat.core.handler.WxAccountType;

import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.message.bean.WxXmlMessage;
import me.chanjar.weixin.mp.api.WxMpMessageMatcher;

/**
 * @author Debenson
 * @since 0.1
 */
public class WxMessageRouterRule {

  private final WxMessageRouter routerBuilder;
  private boolean async = true;
  private String fromUser;
  private String msgType;
  private String event;
  private String eventKey;
  private String content;
  private String rContent;
  private String infoType;
  private int priority;
  private WxAccountType accountType;
  private WxMpMessageMatcher matcher;
  private boolean reEnter = false;
  private List<WxMessageHandler> handlers = new ArrayList<>();
  private List<WxMessageInterceptor> interceptors = new ArrayList<>();

  public WxMessageRouterRule(WxMessageRouter routerBuilder) {
    this.routerBuilder = routerBuilder;
  }

  /**
   * 设置是否异步执行，默认是true
   */
  public WxMessageRouterRule async(boolean async) {
    this.async = async;
    return this;
  }

  /**
   * 如果msgType等于某值
   */
  public WxMessageRouterRule msgType(String msgType) {
    this.msgType = msgType;
    return this;
  }

  /**
   * 如果event等于某值
   */
  public WxMessageRouterRule event(String event) {
    this.event = event;
    return this;
  }

  /**
   * 如果eventKey等于某值
   */
  public WxMessageRouterRule eventKey(String eventKey) {
    this.eventKey = eventKey;
    return this;
  }

  /**
   * 如果content等于某值
   */
  public WxMessageRouterRule content(String content) {
    this.content = content;
    return this;
  }

  /**
   * 如果content匹配该正则表达式
   */
  public WxMessageRouterRule rContent(String regex) {
    this.rContent = regex;
    return this;
  }

  /**
   * 如果fromUser等于某值
   */
  public WxMessageRouterRule fromUser(String fromUser) {
    this.fromUser = fromUser;
    return this;
  }

  /**
   * 如果消息匹配某个matcher，用在用户需要自定义更复杂的匹配规则的时候
   */
  public WxMessageRouterRule matcher(WxMpMessageMatcher matcher) {
    this.matcher = matcher;
    return this;
  }

  /**
   * 如果infoType等于某值
   * 
   * @param infoType
   * @return
   */
  public WxMessageRouterRule infoType(String infoType) {
    this.infoType = infoType;
    return this;
  }

  /**
   * 如果账户类型等于某值
   * 
   * @param type
   * @return
   */
  public WxMessageRouterRule accountType(WxAccountType type) {
    this.accountType = type;
    return this;
  }

  /**
   * 规则优先级
   * 
   * @param priority
   *          值越大优先级越高。
   * @return
   */
  public WxMessageRouterRule priority(int priority) {
    this.priority = priority;
    return this;
  }

  /**
   * 设置微信消息拦截器
   */
  public WxMessageRouterRule interceptor(WxMessageInterceptor interceptor) {
    return interceptor(interceptor, (WxMessageInterceptor[]) null);
  }

  /**
   * 设置微信消息拦截器
   */
  public WxMessageRouterRule interceptor(WxMessageInterceptor interceptor,
      WxMessageInterceptor... otherInterceptors) {
    this.interceptors.add(interceptor);
    if (otherInterceptors != null && otherInterceptors.length > 0) {
      for (WxMessageInterceptor i : otherInterceptors) {
        this.interceptors.add(i);
      }
    }
    return this;
  }

  /**
   * 设置微信消息处理器
   */
  public WxMessageRouterRule handler(WxMessageHandler... handlers) {
    if (handlers != null && handlers.length > 0) {
      for (WxMessageHandler i : handlers) {
        this.handlers.add(i);
      }
    }
    return this;
  }

  /**
   * 规则结束，代表如果一个消息匹配该规则，那么它将不再会进入其他规则
   */
  public WxMessageRouter end() {
    this.routerBuilder.addRule(this);
    return this.routerBuilder;
  }

  /**
   * 规则结束，但是消息还会进入其他规则
   */
  public WxMessageRouter next() {
    this.reEnter = true;
    return end();
  }

  /**
   * 将微信自定义的事件修正为不区分大小写, 比如框架定义的事件常量为click，但微信传递过来的却是CLICK
   */
  protected boolean test(WxXmlMessage wxMessage) {
    return (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUser()))
        && (this.msgType == null || this.msgType.toLowerCase()
            .equals((wxMessage.getMsgType() == null ? null : wxMessage.getMsgType().toLowerCase())))
        && (this.event == null || this.event.toLowerCase()
            .equals((wxMessage.getEvent() == null ? null : wxMessage.getEvent().toLowerCase())))
        && (this.eventKey == null
            || this.eventKey.toLowerCase().equals((wxMessage.getEventKey() == null ? null
                : wxMessage.getEventKey().toLowerCase())))
        && (this.infoType == null
            || this.infoType.toLowerCase().equals(wxMessage.getInfoType() == null ? null
                : wxMessage.getInfoType().toLowerCase()))
        && (this.content == null || this.content.equals(wxMessage.getContent() == null ? null
            : wxMessage.getContent().trim()))
        && (this.rContent == null
            || Pattern.matches(this.rContent, wxMessage.getContent() == null ? ""
                : wxMessage.getContent().trim()))
        && (this.matcher == null || this.matcher.match(wxMessage));
  }

  /**
   * 处理微信推送过来的消息
   *
   * @param wxMessage
   * @return 处理结果
   */
  protected String service(WxXmlMessage wxMessage) throws WxErrorException {
    Map<String, Object> context = new HashMap<>();
    // 如果拦截器不通过
    for (WxMessageInterceptor interceptor : this.interceptors) {
      if (!interceptor.intercept(wxMessage, context)) {
        return null;
      }
    }

    // 交给handler处理
    String res = null;
    for (WxMessageHandler handler : this.handlers) {
      // 返回最后handler的结果
      if (handler == null) {
        continue;
      }
      res = handler.handle(wxMessage, context);
    }
    return res;
  }

  public WxMessageRouter getRouterBuilder() {
    return this.routerBuilder;
  }

  public boolean isAsync() {
    return this.async;
  }

  public void setAsync(boolean async) {
    this.async = async;
  }

  public String getFromUser() {
    return this.fromUser;
  }

  public void setFromUser(String fromUser) {
    this.fromUser = fromUser;
  }

  public String getMsgType() {
    return this.msgType;
  }

  public void setMsgType(String msgType) {
    this.msgType = msgType;
  }

  public String getEvent() {
    return this.event;
  }

  public void setEvent(String event) {
    this.event = event;
  }

  public String getEventKey() {
    return this.eventKey;
  }

  public void setEventKey(String eventKey) {
    this.eventKey = eventKey;
  }

  public String getContent() {
    return this.content;
  }

  public void setContent(String content) {
    this.content = content;
  }

  public String getrContent() {
    return this.rContent;
  }

  public void setrContent(String rContent) {
    this.rContent = rContent;
  }

  public WxMpMessageMatcher getMatcher() {
    return this.matcher;
  }

  public void setMatcher(WxMpMessageMatcher matcher) {
    this.matcher = matcher;
  }

  public boolean isReEnter() {
    return this.reEnter;
  }

  public void setReEnter(boolean reEnter) {
    this.reEnter = reEnter;
  }

  public String getInfoType() {
    return infoType;
  }

  public void setInfoType(String infoType) {
    this.infoType = infoType;
  }

  public List<WxMessageHandler> getHandlers() {
    return this.handlers;
  }

  public void setHandlers(List<WxMessageHandler> handlers) {
    this.handlers = handlers;
  }

  public List<WxMessageInterceptor> getInterceptors() {
    return this.interceptors;
  }

  public void setInterceptors(List<WxMessageInterceptor> interceptors) {
    this.interceptors = interceptors;
  }

  public int getPriority() {
    return priority;
  }

  public WxAccountType getAccountType() {
    return accountType;
  }

}
